<?php

/**
 * 登录、退出、记录日志相关
 * @author yupoxiong<i@yufuping.com>
 */

namespace app\admin\traits;

use app\admin\model\AdminUser;
use app\admin\service\SystemLogService;
use Exception;
use tools\Crypt;
use tools\SafeCookie;
use think\facade\Session;
use think\facade\Cookie;
use think\helper\Str;
use think\facade\Cache;

trait AdminAuth
{
    public static $admin_user_id = 'admin_user_id';
    public static $admin_user_id_sign = 'admin_user_sign';
    protected  $limitKeyPrefix = 'admin_login_count_';
    protected  $loginDeviceKey = 'admin_user_current_login_device_id_';

    /**
     * @var string 保存登录用户信息cookie和session的[ID]key值
     */
    protected $store_uid_key = 'admin_user_id';
    /**
     * @var string 保存登录用户信息cookie和session的[签名]key值
     */
    protected $store_sign_key = 'admin_user_sign';
    /**
     * @var mixed|string 用来签名加密/解密的key
     */
    protected $admin_key = '_ThisClassDefaultKey_';

    //是否登录
    protected function isLogin()
    {
        //这里要写个判断数据库的才行
        $admin_user_id = Session::get(self::$admin_user_id);
        $user       = false;
        $this->user = &$user;
        if (empty($admin_user_id)) {
            if (SafeCookie::has(self::$admin_user_id) && SafeCookie::has(self::$admin_user_id_sign)) {
                $admin_user_id = SafeCookie::get(self::$admin_user_id);
                $sign          = SafeCookie::get(self::$admin_user_id_sign);
                $user          = AdminUser::find($admin_user_id);

                if ($user && $user->sign_str === $sign) {
                    Session::set(self::$admin_user_id, $admin_user_id);
                    Session::set(self::$admin_user_id_sign, $sign);
                    return true;
                }
            }
            return false;
        }

        $user = AdminUser::find($admin_user_id);
        if (!$user) {
            return false;
        }
        $this->uid = $user->id;

        return Session::get(self::$admin_user_id_sign) === $user->sign_str;
    }

    /**
     * session 与cookie登录
     * @param $user AdminUser
     * @param bool $remember
     * @return bool
     */
    public static function authLogin($user, $remember = false)
    {

        Session::set(self::$admin_user_id, $user->id);
        Session::set(self::$admin_user_id_sign, $user->sign_str);

        //记住登录
        if ($remember === true) {
            SafeCookie::set(self::$admin_user_id, $user->id);
            SafeCookie::set(self::$admin_user_id_sign, $user->sign_str);
        } else if (SafeCookie::has(self::$admin_user_id) || SafeCookie::has(self::$admin_user_id_sign)) {
            SafeCookie::delete(self::$admin_user_id);
            SafeCookie::delete(self::$admin_user_id_sign);
        }
        return true;
    }

    //退出
    public static function authLogout()
    {
        Session::delete(self::$admin_user_id);
        Session::delete(self::$admin_user_id_sign);
        if (SafeCookie::has(self::$admin_user_id) || SafeCookie::has(self::$admin_user_id_sign)) {
            SafeCookie::delete(self::$admin_user_id);
            SafeCookie::delete(self::$admin_user_id_sign);
        }
        return true;
    }


    //创建操作日志
    public function createAdminLog($user, $name)
    {
        $data_arr = [
            'header' => request()->header(),
            'param'  => request()->param(),
        ];
        //加密数据，防脱库
        $crypt_data = Crypt::encrypt(json_encode($data_arr, JSON_THROW_ON_ERROR), setting('admin.safe.admin_key'));

        $data = [
            'admin_user_id' => $user->id,
            'name'          => $name,
            'method'    => request()->method(),
            'url'           => request()->url(),
            'ip'        => getRealIp(),
            'useragent'   => $_SERVER['HTTP_USER_AGENT'],
            'content' => $crypt_data,
            'create_time' => time(),
        ];
        SystemLogService::instance()->save($data);
    }

    /**
     * 设置登录的设备ID
     * @param $user
     * @return bool
     */
    public function setLoginDeviceId($user)
    {
        $device_id = $this->getDeviceId($user);
        $cache_key = $this->loginDeviceKey . $user->id;
        return Cache::set($cache_key, $device_id);
    }

    /**
     * 获取当前设备ID
     * @param $admin_user
     * @return string
     */
    public function getDeviceId($admin_user)
    {
        $key       = 'device_id_uid_' . $admin_user->id;
        $device_id = Cookie::get($key);
        if (!$device_id) {
            try {
                $rand_text = Str::random(20);
            } catch (Exception $e) {
                $rand_text = time() . $admin_user->id . microtime();
            }

            $device_id = sha1('admin_user_' . $admin_user->id . $rand_text . time());
            Cookie::set($key, $device_id);
        }

        return $device_id;
    }

    /**
     * 获取当前登录的设备ID
     * @param $user
     * @return string
     */
    public function getLoginDeviceId($user)
    {
        $cache_key = $this->loginDeviceKey . $user->id;

        return (string)Cache::get($cache_key);
    }


    /**
     * 获取签名
     * @param $admin_user
     * @return string
     */
    public function getUserSign($admin_user): string
    {
        $this->construct();
        return md5(md5($this->admin_key . $admin_user->id) . $this->admin_key);
    }


    /**
     * 单设备登录检查
     * @return bool
     */
    public function checkOneDeviceLogin()
    {
        $request = request();
        $check   = (int)setting('admin.safe.one_device_login');
        if (!$check) {
            return true;
        }
        $action = strtolower($request->action());
        $authExcept = !empty($this->authExcept) ? array_map('strtolower', $this->authExcept) : $this->authExcept;

        if (in_array($action, $authExcept, true)) {
            return true;
        }
        $device_id = $this->getDeviceId($this->user);
        $login_device_id = $this->getLoginDeviceId($this->user);

        if ($login_device_id && $login_device_id !== $device_id) {
            $this->authLogout();

            $login_url = url('/admin/auth/login')->build();
            if ($request->isGet()) {
                return redirect($login_url);
            }

            return redirect(url('/admin/error/index', ['msg' => '未登录']));

            // throw new HttpResponseException(error('未登录',  $login_url, [], 401));
        }

        return $this->setLoginDeviceId($this->user);
    }

    /**
     * 检测登录限制
     *
     */
    public function checkLoginLimit(): bool
    {
        $login = setting('admin.login');
        $is_limit = (int)$login['login_limit'];
        if ($is_limit) {
            // 最大错误次数
            $max_count        = (int)$login['login_max_count'];
            $login_limit_hour = (int)$login['login_limit_hour'];
            // 缓存key
            $cache_key  = $this->limitKeyPrefix . md5(request()->ip());
            $have_count = (int)Cache::get($cache_key);
            if ($have_count >= $max_count) {
                throw new Exception('连续' . $max_count . '次登录失败，请' . $login_limit_hour . '小时后再试');
            }
            return true;
        }
        return true;
    }

    /**
     * 设置登录限制
     * @return bool
     */
    public function setLoginLimit(): bool
    {
        $login = setting('admin.login');

        $is_limit = (int)$login['login_limit'];
        if ($is_limit) {
            // 最大错误次数
            $login_limit_hour = (int)$login['login_limit_hour'];
            // 缓存key
            $cache_key = $this->limitKeyPrefix . md5(request()->ip());
            if (Cache::has($cache_key)) {
                Cache::inc($cache_key);
                return true;
            }
            Cache::set($cache_key, 1, $login_limit_hour * 3600);
        }
        return true;
    }
    public function construct()
    {
        $safe = setting('admin.safe');
        $this->admin_key      = $safe['admin_key'] ?? $this->admin_key;
        $this->store_uid_key  = $safe['store_uid_key'] ?? $this->store_uid_key;
        $this->store_sign_key = $safe['store_sign_key'] ?? $this->store_sign_key;
    }
}
